using System;
using System.Collections.Generic;
using System.Linq;
using Xnaml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Text;

namespace Xnaml.Demo {
	/// <summary>
	/// All you have to do is extend Xnaml.Game and then run.
	/// The base class has been written to look, feel, and even smell exactly
	/// like Microsoft.Xna.Framework.Game.
	/// </summary>
	public class XPG : Xnaml.Game {

		/// <summary>
		/// The GraphicsDeviceManager has been designed to
		/// look, feel, and work almost exactly like
		/// Microsft.Xna.Framework's one, giving one access to
		/// properties like SynchronizeWithVerticalRetrace
		/// and IsFullScreen. You can apply changes even, with
		/// graphics.ApplyChanges();
		/// The internals of the class are also mostly protected,
		/// so you may overwrite to your Heart's Content.
		/// </summary>
		
		Xnaml.GraphicsDeviceManager graphics;
		SpriteBatch spritebatch;
		SpriteFont font;
		StringBuilder status;
		Texture2D background;
		Gohan gohan;
		IntInterpolator backgroundalpha;
		bool needbroadcast;
		double powerlevel;

		public XPG ( ) {
			// When working in WPF, XAML will try to instantiate an instance of
			// the class to do drawing with. Thus, a property - DesignTime -
			// will let you know if you're actually running your game
			// [ DesignTime = false ] or if it is just XAML trying to make a
			// class
			if ( !DesignTime ) {
				// The GraphicsDeviceManager was made to work exactly like Microsoft's,
				// so just call the constructor with the current game
				// like in every other example
				graphics = new Xnaml.GraphicsDeviceManager( this );
				// Content is identical
				Content.RootDirectory = "Content";
				status = new StringBuilder( );
				gohan = new Gohan( );
				backgroundalpha = new IntInterpolator( 15 );
				status.Append( "[ Waiting for WPF Message ]" );
				needbroadcast = false;
				powerlevel = 0;
			}

			// After this, Everything is the same, expect with more features for
			// Interacting with WPF. See the overridden ReceiveMessage function below.
		}

		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		protected override void Initialize ( ) {
			// TODO: Add your initialization logic here

			base.Initialize( );
		}

		/// <summary>
		/// LoadContent will be called once per game and is the place to load
		/// all of your content.
		/// </summary>
		protected override void LoadContent ( ) {
			// Create a new SpriteBatch, which can be used to draw textures.
			spritebatch = new SpriteBatch( GraphicsDevice );

			// TODO: use this.Content to load your game content here
			background = Content.Load<Texture2D>( "NightTemple" );
			font = Content.Load<SpriteFont>( "Font" );
			gohan.Load( Content );
		}

		/// <summary>
		/// UnloadContent will be called once per game and is the place to unload
		/// all content.
		/// </summary>
		protected override void UnloadContent ( ) {
			// TODO: Unload any non ContentManager content here
		}

		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input, and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Update ( GameTime gameTime ) {
			// Allows the game to exit
			if ( Keyboard.GetState( PlayerIndex.One ).IsKeyDown( Keys.Escape ) )
				this.Exit( );

			// TODO: Add your update logic here
			backgroundalpha.Update( gameTime );

			if ( backgroundalpha.Finished && needbroadcast ) {
				BroadcastMessage( this, "Broadcasting this event: Gohan has entered the Temple! Move with A and D." );
				status.Clear( );
				status.Append( "[ Message broadcasted ]" );
				needbroadcast = false;
			}

			if ( gohan.Alpha.Finished ) {
				if ( KeyboardFocused ) {
					KeyboardState keystate = Keyboard.GetState( PlayerIndex.One );
					if ( keystate.IsKeyDown( Keys.A ) ) {
						gohan.Position.X -= 1;
						gohan.Animate( "Walk", 150, true, false );
						gohan.spriteeffects |= SpriteEffects.FlipHorizontally;
					}
					else if ( keystate.IsKeyDown( Keys.D ) ) {
						gohan.Position.X += 1;
						gohan.Animate( "Walk", 150, true, false );
						gohan.spriteeffects &= ~SpriteEffects.FlipHorizontally;
					}
					else if ( gohan.State != "Power Up" ) {
						gohan.Animate( "", 300, true, true );
					}
					else {
						powerlevel += 1;
						status.Clear( );
						status.Append( String.Format( "Power Level: {0}", powerlevel.ToString("0.00") ) );
						if ( powerlevel > 9000 ) {
							status.AppendLine( "It's... I-It's over NINE THOUSAND!!!" );
						}
					}
				}
			}

			gohan.Update( gameTime );

			if ( gohan.Position.X < 0 )
				gohan.Position.X = 0;
			if ( gohan.Position.X + gohan.Size.X > GraphicsDevice.Viewport.Bounds.Width )
				gohan.Position.X = GraphicsDevice.Viewport.Bounds.Width - gohan.Size.X;

			gohan.Position.Y = GraphicsDevice.Viewport.Bounds.Bottom - gohan.Size.Y - GraphicsDevice.Viewport.Bounds.Height * 0.10f;

			base.Update( gameTime );
		}

		/// <summary>
		/// An aribtrary function for getting data from a SendMessage() call.
		/// This is useful only when the Game is in a separate asembly/library
		/// and cannot communicate with the WPF Application directly.
		/// </summary>
		/// <typeparam name="T">The type of data to the game will recieve when SendMessage() is called on Game.</typeparam>
		/// <param name="sender">The sender of the data.</param>
		/// <param name="data">The data retrieved from SendMessage().</param>
		protected override void MessageReceived<T> ( object sender, ref T data ) {
			if ( data is string ) {
				string state = data as string;
				double msperframe = 250;
				bool repeat = true;
				bool reverse = true;
				if ( state == "Appear" && gohan.Alpha.Progress == 0 ) {
					msperframe = 150;
					reverse = false;
					repeat = false;
					status.Clear( );
					status.Append( "[ Warping in Gohan ]" );
					gohan.Alpha.Start( 1, 2250 );
					gohan.Animate( data as string, msperframe, repeat, reverse );
					backgroundalpha.Start( 100, 2250 );
					needbroadcast = true;
				}
				else {
					gohan.Animate( data as string, 100, repeat, reverse );
					BroadcastMessage( this, "Must.... Reach.... 9000!" );
				}
			}
		}

		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Draw ( GameTime gameTime ) {
			GraphicsDevice.Clear( Color.Black );

			spritebatch.Begin( );

			spritebatch.Draw( background, GraphicsDevice.Viewport.Bounds, Color.White * (backgroundalpha.Value / 100.0f) );
			spritebatch.DrawString( font, status,
				new Vector2( GraphicsDevice.Viewport.Bounds.Center.X - font.MeasureString( status ).X * 0.5f, 10 ),
				Color.White );
			gohan.Render( spritebatch );
			spritebatch.End( );
			// TODO: Add your drawing code here

			base.Draw( gameTime );
		}
	}
}
